package terra.managers;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Vector;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import terra.Config;
import terra.definitions.*;
import terra.definitions.item.*;
import terra.definitions.Biom.BiomGroundDef;
import terra.definitions.Biom.BiomObjectDef;
import terra.definitions.CraftDef.Ingridient;
import terra.definitions.CraftDef.Result;
import terra.model.Slot;

public class DefManager {

	public static HashMap<Integer,GroundDef> groundDefs= new HashMap<Integer,GroundDef>();
	public static HashMap<Integer,Biom> bioms = new HashMap<Integer,Biom>();	
	public static HashMap<Integer,CraftDef> crafts = new HashMap<Integer,CraftDef>();
	
	public static HashMap<Integer,ItemDef> items = new HashMap<Integer,ItemDef>();
	public static HashMap<Integer,ArmorDef> armors = new HashMap<Integer,ArmorDef>();
	public static HashMap<Integer,WeaponDef> weapons = new HashMap<Integer,WeaponDef>();
	public static HashMap<Integer,ItemDef> allItems = new HashMap<Integer,ItemDef>();
	public static HashMap<Integer,NpcDef> npcDefs = new HashMap<Integer,NpcDef>();
	
	public static HashMap<Integer,WeatherDef> weatherDefs = new HashMap<Integer,WeatherDef>();

	
	public static void init(){
		loadBlockDefs();
		loadItems();
		loadBioms();
		loadCrafts();
		loadNpcs();
		loadWeather();
	}

	private static void loadWeather() {
		Document doc = getDocumentForFile("weather.xml");
		if (doc ==null)
			return;
        NodeList objects = doc.getElementsByTagName("weather");
        for(int s=0; s<objects.getLength() ; s++) {
            Node obj = objects.item(s);
            if (obj.getAttributes() != null) {
            	WeatherDef def = new WeatherDef();
            	for(int i=0; i< obj.getAttributes().getLength() ; i++) {
            		Node n = obj.getAttributes().item(i);
            		if (n.getNodeName().equalsIgnoreCase("name"))
            			def.name = n.getNodeValue();	
            		else if (n.getNodeName().equalsIgnoreCase("id"))
                    	def.id =  Byte.parseByte(n.getNodeValue());
            		else if (n.getNodeName().equalsIgnoreCase("minTime"))
                    	def.minTime =  Integer.parseInt(n.getNodeValue());
            		else if(n.getNodeName().equalsIgnoreCase("maxTime"))
                		def.maxTime = Integer.parseInt(n.getNodeValue());
            		else if(n.getNodeName().equalsIgnoreCase("minIntensity"))
                		def.minIntensity = Float.parseFloat(n.getNodeValue());
            		else if(n.getNodeName().equalsIgnoreCase("maxIntensity"))
                		def.maxIntensity = Float.parseFloat(n.getNodeValue());
            		else if(n.getNodeName().equalsIgnoreCase("minDayLighting"))
                		def.minDayLighting = Float.parseFloat(n.getNodeValue());
            		else if(n.getNodeName().equalsIgnoreCase("maxDayLighting"))
                		def.maxDayLighting = Float.parseFloat(n.getNodeValue());
            		else if(n.getNodeName().equalsIgnoreCase("maxDayLighting"))
                		def.maxDayLighting = Float.parseFloat(n.getNodeValue());
            		else if(n.getNodeName().equalsIgnoreCase("needUpdate"))
            			def.needUpdate = Boolean.parseBoolean(n.getNodeValue());
            		else
            			def.addProperty(n.getNodeName(), n.getNodeValue());
            		/*
            		 * 	public String name;
		public int id;
		public long minTime, maxTime;
		public float intensity;
		public float dayLighting;
		public float angle = 0f;
            		 */
            	}
            	weatherDefs.put((int) def.id, def);
            }

        }
        System.out.println("DefManager : Weather definitions loaded : " + weatherDefs.size());
		
	}

	static Document getDocumentForFile(String file){
		InputStream is = null;
		try {
			File f = new File("data" + File.separatorChar+file);
			if (f.exists())
				is = new FileInputStream(f);
			else
				is = Config.class.getResourceAsStream(file);
		} catch (FileNotFoundException e1) {
			System.out.println(new File("data" + File.separatorChar+file).getAbsolutePath());
			e1.printStackTrace();
		}
        DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder docBuilder = null;
        Document doc = null;
		try {
			docBuilder = docBuilderFactory.newDocumentBuilder();
			doc = docBuilder.parse (is);
			doc.getDocumentElement ().normalize ();
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return doc;
	}
	
	public static Vector<DropCategory> loadDrop(Node node) {
		Vector<DropCategory> list = new Vector<DropCategory>();
		for (Node catNode = node.getFirstChild(); catNode != null; catNode = catNode.getNextSibling()) 	{
        	if (catNode.getNodeName() == "category") {
        		DropCategory cat = new DropCategory(Integer.parseInt(catNode.getAttributes().getNamedItem("id").getNodeValue()));
        		for (Node itemNode = catNode.getFirstChild(); itemNode != null; itemNode = itemNode.getNextSibling()) {
                	if (itemNode.getNodeName() == "item") {
	        			DropData data = new DropData();
	        			data.setItemId(Integer.parseInt(itemNode.getAttributes().getNamedItem("id").getNodeValue()));
	        			data.setChance((int) (Float.parseFloat(itemNode.getAttributes().getNamedItem("chance").getNodeValue())*1000));
	        			data.setMinDrop(Integer.parseInt(itemNode.getAttributes().getNamedItem("minCount").getNodeValue()));
	        			data.setMaxDrop(Integer.parseInt(itemNode.getAttributes().getNamedItem("maxCount").getNodeValue()));
	        			cat.addDropData(data);
                	}
        		}
        		list.add(cat);
        	}
    	}
		return list;
	}
	
	static void loadCrafts(){
		Document doc = getDocumentForFile("craft.xml");
		if (doc ==null)
			return;
        NodeList objects = doc.getElementsByTagName("craft");
        for(int s=0; s<objects.getLength() ; s++) {
            Node obj = objects.item(s);
            if (obj.getAttributes() != null) {
            	CraftDef def = new CraftDef();
            	def.name =  obj.getAttributes().getNamedItem("name").getNodeValue();
            	def.recipeId =  Integer.parseInt(obj.getAttributes().getNamedItem("recipeId").getNodeValue());
            	def.ingridients = new ArrayList<Ingridient>();
            	def.results = new ArrayList<Result>();
        		for (Node n = obj.getFirstChild(); n != null; n = n.getNextSibling())
        			if ("consume".equalsIgnoreCase(n.getNodeName())) {
        				Ingridient i = def.new Ingridient();
        				i.itemId =Integer.parseInt(n.getAttributes().getNamedItem("itemId").getNodeValue());
        				i.count = Integer.parseInt(n.getAttributes().getNamedItem("count").getNodeValue());
        				if (Boolean.parseBoolean(n.getAttributes().getNamedItem("isMain").getNodeValue()))
        					def.mainIngridient = i.itemId;
        				def.ingridients.add(i);
        			} else if ("result".equalsIgnoreCase(n.getNodeName())) {
        				Result i = def.new Result();
        				i.itemId =Integer.parseInt(n.getAttributes().getNamedItem("itemId").getNodeValue());
                    	i.minCount =  Integer.parseInt(n.getAttributes().getNamedItem("minCount").getNodeValue());
                    	i.maxCount =  Integer.parseInt(n.getAttributes().getNamedItem("maxCount").getNodeValue());
                    	i.chance =  Integer.parseInt(n.getAttributes().getNamedItem("chance").getNodeValue());
        				def.results.add(i);
        			}
            	crafts.put(def.recipeId, def);
            }
        }
        System.out.println("DefManager : Craft definitions loaded : " + groundDefs.size());
	}
	
	static void loadItems() {
		Document doc = getDocumentForFile("items.xml");
		if (doc ==null)
			return;
        NodeList objects = doc.getElementsByTagName("item");
        for(int s=0; s<objects.getLength() ; s++){
            Node obj = objects.item(s);
            if (obj.getAttributes() != null){
            	if (obj.getAttributes().getNamedItem("type").getNodeValue().equalsIgnoreCase("other")
            	|| obj.getAttributes().getNamedItem("type").getNodeValue().equalsIgnoreCase("light")) {
            		loadItem(obj);
            	}
            	if (obj.getAttributes().getNamedItem("type").getNodeValue().equalsIgnoreCase("armor")) {
            		loadArmor(obj);
            	}
            	if (obj.getAttributes().getNamedItem("type").getNodeValue().equalsIgnoreCase("weapon"))	{
            		loadWeapon(obj);
            	}
            }
        }
        
        int count = items.size()+armors.size()+weapons.size();
        //Server.getInstance().logger.info("ItemManager : total loaded " + count);
        System.out.println("DefManager : Items definitions loaded " + count);
	}


	static void loadWeapon(Node obj) {
		WeaponDef item = new WeaponDef();
		item.itemType = obj.getAttributes().getNamedItem("type").getNodeValue();
        for(int s=0; s<obj.getChildNodes().getLength() ; s++) {
        	 Node node = obj.getChildNodes().item(s);
        	 if (node.getNodeName() == "id")
        		 item.id = Integer.parseInt(node.getTextContent());
        	 else if (node.getNodeName() == "name")
        		 item.name = node.getTextContent();
        	 else if (node.getNodeName() == "image")
        		 item.image = node.getTextContent();
        	 else if (node.getNodeName() == "tooltip")
        		 item.tooltip = node.getTextContent();
        	 else if (node.getNodeName() == "stackable")
        		 item.isStackable = Boolean.parseBoolean(node.getTextContent());
        	 else if (node.getNodeName() == "dropable")
        		 item.isDropable = Boolean.parseBoolean(node.getTextContent());
        	 else if (node.getNodeName() == "removable")
        		 item.isRemovable = Boolean.parseBoolean(node.getTextContent());
        	 else if (node.getNodeName() == "damage")
        		 item.damage = Integer.parseInt(node.getTextContent());
        	 else if (node.getNodeName() == "radius")
        		 item.radius = Integer.parseInt(node.getTextContent());
        	 else if (node.getNodeName() == "weight")
        		 item.weight = Integer.parseInt(node.getTextContent());
        	 else if (node.getNodeName() == "slot")
        		 item.slot = Slot.getSlotByName(node.getTextContent());
        	 else if (node.getNodeName() == "type")
        		 item.type = getWeaponTypeForString(node.getTextContent());
        	 else if (node.getNodeName() == "radius")
        		 item.radius = Integer.parseInt(node.getTextContent());
        	 else if (node.getNodeName() == "armor")
        		 item.armor = Integer.parseInt(node.getTextContent());
        	 else if (node.getNodeName() == "pickupable")
        		 item.isPickupable = Boolean.parseBoolean(node.getTextContent());
        	 else
        		 item.addProperty(node.getNodeName(), node.getTextContent());
		}
        weapons.put(item.id,item);
        allItems.put(item.id,item);
	}


	static void loadArmor(Node obj) {
		ArmorDef item = new ArmorDef();
		item.itemType = obj.getAttributes().getNamedItem("type").getNodeValue();
        for(int s=0; s<obj.getChildNodes().getLength() ; s++){
        	 Node node = obj.getChildNodes().item(s);
        	 if (node.getNodeName() == "id")
        		 item.id = Integer.parseInt(node.getTextContent());
        	 else if (node.getNodeName() == "name")
        		 item.name = node.getTextContent();
        	 else if (node.getNodeName() == "image")
        		 item.image = node.getTextContent();
        	 else if (node.getNodeName() == "tooltip")
        		 item.tooltip = node.getTextContent();
        	 else if (node.getNodeName() == "stackable")
        		 item.isStackable = Boolean.parseBoolean(node.getTextContent());
        	 else if (node.getNodeName() == "dropable")
        		 item.isDropable = Boolean.parseBoolean(node.getTextContent());
        	 else if (node.getNodeName() == "removable")
        		 item.isRemovable = Boolean.parseBoolean(node.getTextContent());
        	 else if (node.getNodeName() == "armor")
        		 item.armor = Integer.parseInt(node.getTextContent());
        	 else if (node.getNodeName() == "slot")
        		 item.slot = Slot.getSlotByName(node.getTextContent());
        	 else if (node.getNodeName() == "weight")
        		 item.weight = Integer.parseInt(node.getTextContent());
        	 else if (node.getNodeName() == "pickupable")
        		 item.isPickupable = Boolean.parseBoolean(node.getTextContent());
        	 else
        		 item.addProperty(node.getNodeName(), node.getTextContent());
		}
        armors.put(item.id,item);
        allItems.put(item.id,item);
	}


	static void loadItem(Node obj) {
		ItemDef item = new ItemDef();
		item.itemType = obj.getAttributes().getNamedItem("type").getNodeValue();
        for(int s=0; s<obj.getChildNodes().getLength() ; s++) {
        	 Node node = obj.getChildNodes().item(s);
        	 if (node.getNodeName() == "id")
        		 item.id = Integer.parseInt(node.getTextContent());
        	 else if (node.getNodeName() == "name")
        		 item.name = node.getTextContent();
        	 else if (node.getNodeName() == "image")
        		 item.image = node.getTextContent();
        	 else if (node.getNodeName() == "tooltip")
        		 item.tooltip = node.getTextContent();
        	 else if (node.getNodeName() == "stackable")
        		 item.isStackable = Boolean.parseBoolean(node.getTextContent());
        	 else if (node.getNodeName() == "dropable")
        		 item.isDropable = Boolean.parseBoolean(node.getTextContent());
        	 else if (node.getNodeName() == "removable")
        		 item.isRemovable = Boolean.parseBoolean(node.getTextContent());
        	 else if (node.getNodeName() == "weight")
        		 item.weight = Integer.parseInt(node.getTextContent());
        	 else if (node.getNodeName() == "construct")
        		 item.isConstruct = Boolean.parseBoolean(node.getTextContent());
        	 else if (node.getNodeName() == "constructGroundId")
        		 item.constructGroundId = Integer.parseInt(node.getTextContent());
        	 else if (node.getNodeName() == "constructObjectId")
        		 item.constructObjectId = Integer.parseInt(node.getTextContent());
        	 else if (node.getNodeName() == "instance")
        		 item.instance = node.getTextContent();	
        	 else if (node.getNodeName() == "pickupable")
        		 item.isPickupable = Boolean.parseBoolean(node.getTextContent());
        	 else
        		 item.addProperty(node.getNodeName(), node.getTextContent());
        	 
		}
        items.put(item.id,item);
        allItems.put(item.id,item);
	}
	
	
	static void loadBlockDefs() {
		Document doc = getDocumentForFile("ground.xml");
		if (doc ==null)
			return;
        NodeList objects = doc.getElementsByTagName("ground");
        for(int s=0; s<objects.getLength() ; s++) {
            Node obj = objects.item(s);
            if (obj.getAttributes() != null) {
            	GroundDef def = new GroundDef();
            	for(int i=0; i< obj.getAttributes().getLength() ; i++) {
            		Node n = obj.getAttributes().item(i);
            		if (n.getNodeName().equalsIgnoreCase("image"))
            			def.image = n.getNodeValue();	
            		else if (n.getNodeName().equalsIgnoreCase("baseHp"))
                    	def.baseHp =  Integer.parseInt(n.getNodeValue());
            		else if (n.getNodeName().equalsIgnoreCase("id"))
                    	def.id =  Byte.parseByte(n.getNodeValue());
            		else if (n.getNodeName().equalsIgnoreCase("itemId"))
                    	def.itemId =  Integer.parseInt(n.getNodeValue());
            		else if(n.getNodeName().equalsIgnoreCase("resourceImage"))
                		def.resourceImage = n.getNodeValue();
            		else
            			def.addProperty(n.getNodeName(), n.getNodeValue());
            	}

        		for (Node n = obj.getFirstChild(); n != null; n = n.getNextSibling()) {
                 	 if (n.getNodeName() == "drop")
                 		def.drop = loadDrop(n);
        		}
            	groundDefs.put((int) def.id, def);
            }

        }
        System.out.println("DefManager : Ground definitions loaded : " + groundDefs.size());
	}
	
	static void loadBioms(){
        Document doc = getDocumentForFile("bioms.xml");
		if (doc ==null)
			return;
        NodeList objects = doc.getElementsByTagName("biom");
        for(int s=0; s<objects.getLength() ; s++){
            Node obj = objects.item(s);
            if (obj.getAttributes() != null){
            	Biom b = new Biom();
            	b.name =  obj.getAttributes().getNamedItem("name").getNodeValue();
            	b.id =  Integer.parseInt(obj.getAttributes().getNamedItem("id").getNodeValue());
            	b.minHeight =  Integer.parseInt(obj.getAttributes().getNamedItem("minHeight").getNodeValue());
            	b.maxHeight =  Integer.parseInt(obj.getAttributes().getNamedItem("maxHeight").getNodeValue());
        		for (Node n = obj.getFirstChild(); n != null; n = n.getNextSibling()) {
        			if ("ground".equalsIgnoreCase(n.getNodeName())) {
        				BiomGroundDef i = b.new BiomGroundDef();
        				i.groundId = Integer.parseInt(n.getAttributes().getNamedItem("id").getNodeValue());
                    	i.minHeight =  Integer.parseInt(n.getAttributes().getNamedItem("minHeight").getNodeValue());
                    	i.maxHeight =  Integer.parseInt(n.getAttributes().getNamedItem("maxHeight").getNodeValue());
                    	i.chance =   (int) (Float.parseFloat(n.getAttributes().getNamedItem("chance").getNodeValue())*1000);
                    	if (n.getAttributes().getNamedItem("isRes") != null)
                    		i.isRes = true;
        				b.grounds.add(i);
        			}else if ("weather".equalsIgnoreCase(n.getNodeName())){
        				b.weathers.put(Integer.parseInt(n.getAttributes().getNamedItem("id").getNodeValue()), Integer.parseInt(n.getAttributes().getNamedItem("chance").getNodeValue()));
        			}else if ("flora".equalsIgnoreCase(n.getNodeName())) {
                		BiomObjectDef floraDef = b.new BiomObjectDef();
                		floraDef.npcDefId = Integer.parseInt(n.getAttributes().getNamedItem("id").getNodeValue());
                		floraDef.minCount =  Integer.parseInt(n.getAttributes().getNamedItem("minCount").getNodeValue());
                		floraDef.maxCount =  Integer.parseInt(n.getAttributes().getNamedItem("maxCount").getNodeValue());
                		b.flora.add(floraDef);
        			}else if ("fauna".equalsIgnoreCase(n.getNodeName())) {
                		BiomObjectDef faunaDef = b.new BiomObjectDef();
                		faunaDef.npcDefId = Integer.parseInt(n.getAttributes().getNamedItem("id").getNodeValue());
                		faunaDef.minCount =  Integer.parseInt(n.getAttributes().getNamedItem("minCount").getNodeValue());
                		faunaDef.maxCount =  Integer.parseInt(n.getAttributes().getNamedItem("maxCount").getNodeValue());
                		b.fauna.add(faunaDef);
        			}else if ("tree".equalsIgnoreCase(n.getNodeName())) {
                		BiomObjectDef floraDef = b.new BiomObjectDef();
                		floraDef.npcDefId = Integer.parseInt(n.getAttributes().getNamedItem("id").getNodeValue());
                		floraDef.minCount =  Integer.parseInt(n.getAttributes().getNamedItem("minCount").getNodeValue());
                		floraDef.maxCount =  Integer.parseInt(n.getAttributes().getNamedItem("maxCount").getNodeValue());
                		b.trees.add(floraDef);
        			}
        		}
        		bioms.put(b.id, b);
            }
        }
        System.out.println("DefManager : Biom definitions loaded : " + bioms.size());
	}
	
	public static void loadNpcs() {
        Document doc = getDocumentForFile("objects.xml");
		if (doc ==null)
			return;
        NodeList objects = doc.getElementsByTagName("npc");
        for(int s=0; s<objects.getLength() ; s++){
            Node obj = objects.item(s);
            if (obj.getAttributes() != null) {
            	NpcDef def = new NpcDef();
            	def.id = Integer.parseInt(obj.getAttributes().getNamedItem("id").getNodeValue());
                for(int i=0; i<obj.getChildNodes().getLength() ; i++) {
                   	 Node node = obj.getChildNodes().item(i);
                   	 if (node.getNodeName() == "drop")
                   		def.drop = loadDrop(node);
                   	 else if (node.getNodeName() == "width") {
                   		 if (node.getTextContent().startsWith("tile")) {
                   			 if (node.getTextContent().length() == 4)
                   				def.addProperty("width",String.valueOf(Config.tileSize));
                   			 else
                   				def.addProperty("width",String.valueOf(Config.tileSize*Integer.parseInt(node.getTextContent().substring(5))));
                   		 }
                   		 else
                   			def.addProperty("width",String.valueOf(Integer.parseInt(node.getTextContent())));
                   	 } else if (node.getNodeName() == "height") {
                   		 if (node.getTextContent().startsWith("tile")) {
                   			 if (node.getTextContent().length() == 4)
                    			def.addProperty("height",String.valueOf(Config.tileSize));
                   			 else
                   				def.addProperty("height",String.valueOf(Config.tileSize*Integer.parseInt(node.getTextContent().substring(5))));
                   		 } else
                   			def.addProperty("height",String.valueOf(Integer.parseInt(node.getTextContent())));
                   	 } else {
                   		def.addProperty(node.getNodeName(),node.getTextContent());
                   	 }
            	}
                npcDefs.put(def.id, def);
           	}
        }
        System.out.println("DefManager : NPC definitions loaded : " + npcDefs.size());
	}
	
	public static int getWeaponTypeForString(String text) {
		if (text.equalsIgnoreCase("close"))
			return 0;
		else if (text.equalsIgnoreCase("range"))
			return 1;
		else if (text.equalsIgnoreCase("spell"))
			return 2;		
		return 0;
	}
	
}
